package com.spring.servlet;

import com.spring.annotation.MyServlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

//loadOnStartup=1:启动的时候加载
@WebServlet(value = "/",loadOnStartup = 1,initParams = {
        @WebInitParam(name="package",value = "com.spring.controller")
})
public class DispatcherServlet extends HttpServlet {
    Map<String, Method> getMapping=new HashMap<>();
    Map<String,Method> postMapping=new HashMap<>();

    @Override
    public void init() throws ServletException {
        String packageName=getInitParameter("package");
        //扫描包下的所有类
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        String path = packageName.replace('.', '/');
        Enumeration<URL> resources=null;
        try {
            resources = classLoader.getResources(path);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        List<Class<?>> classes = new ArrayList<>();

        while (resources.hasMoreElements()) {
            URL resource = resources.nextElement();
            File file = new File(resource.getFile());
            if (file.isDirectory()) {
                for (File classFile : file.listFiles((f) -> f.isFile())) {
                    if (classFile.getName().endsWith(".class")) {
                        String className = packageName + "." + classFile.getName().substring(0, classFile.getName().length() - 6);
                        Class<?> clazz = null;
                        try {
                            clazz = Class.forName(className);
                            // 判断类上是否有 MyServlet 注解
                            MyServlet annotation = clazz.getAnnotation(MyServlet.class);
                            if (annotation != null) {
                                classes.add(clazz);
                            }
                        } catch (ClassNotFoundException e) {
                            throw new RuntimeException(e);
                        }

                    }
                }
            }
        }
        // 遍历所有类，找到 RequestMapping 注解
        for (Class<?> clazz : classes) {
            Method[] declaredMethods = clazz.getDeclaredMethods();
            for (Method method : declaredMethods) {
                com.spring.annotation.RequestMapping annotation = method.getAnnotation(com.spring.annotation.RequestMapping.class);
                if (annotation != null) {
                    String value = annotation.value();
                    String methodValue = annotation.method();
                    System.out.println("value: " + value + ", method: " + methodValue);
                    if ("GET".equalsIgnoreCase(methodValue)) {
                        getMapping.put(value, method);
                    } else if ("POST".equalsIgnoreCase(methodValue)) {
                        postMapping.put(value, method);
                    }
                }
            }
        }
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("DispatcherServlet doGet");
        String requestURI = req.getRequestURI();
        System.out.println(requestURI); // /web/hello 包含上下文路径

        // 将上下文路径去掉 /web/hello -> /hello
        requestURI = requestURI.substring(req.getContextPath().length());

        Method method = null;
        if ("GET".equalsIgnoreCase(req.getMethod())){
            method  = getMapping.get(requestURI);
        } else {
            method = postMapping.get(requestURI);
        }

        if (method == null) {
            resp.sendError(404, "Not Found");
        } else {
            try {
                Object o = method.getDeclaringClass().newInstance();
                method.invoke(o, req, resp);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

}
